package com.webchat.user.service;

import com.alibaba.nacos.common.utils.CollectionUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.webchat.common.bean.APIPageResponseBean;
import com.webchat.common.constants.WebConstant;
import com.webchat.common.enums.APIErrorCommonEnum;
import com.webchat.common.enums.RedisKeyEnum;
import com.webchat.common.enums.RoleCodeEnum;
import com.webchat.common.enums.UserStatusEnum;
import com.webchat.common.exception.BusinessException;
import com.webchat.common.helper.SessionHelper;
import com.webchat.common.service.RedisService;
import com.webchat.common.util.*;
import com.webchat.domain.vo.request.CreateGroupRequestVO;
import com.webchat.domain.vo.request.CreateAccountRequestVO;
import com.webchat.domain.vo.request.CreateRobotRequestVO;
import com.webchat.domain.vo.request.UpdateUserInfoRequestVO;
import com.webchat.domain.vo.response.GroupResponseInfoVO;
import com.webchat.domain.vo.response.UserBaseResponseInfoVO;
import com.webchat.domain.vo.response.UserBaseResponseVO;
import com.webchat.domain.vo.response.UserSafeResponseInfoVO;
import com.webchat.user.repository.dao.IGroupUserDAO;
import com.webchat.user.repository.dao.IUserDAO;
import com.webchat.user.repository.entity.GroupUserEntity;
import com.webchat.user.repository.entity.UserEntity;
import com.webchat.user.service.relation.AccountRelationFactory;
import com.webchat.user.service.relation.User2AIAgentSenderAccountRelationService;
import com.webchat.user.service.relation.User2GroupAccountRelationService;
import com.webchat.user.service.relation.User2ServerAccountRelationService;
import jakarta.persistence.criteria.Predicate;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class UserService {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private HttpServletResponse response;

    @Autowired
    private IUserDAO userDAO;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IGroupUserDAO groupUserDAO;

    @Autowired
    private PublicAccountService publicAccountService;

    /***
     * USER ID 前缀
     */
    private static final String USER_ID_PREFIX = "U";
    private static final String ROBOT_ID_PREFIX = "R";
    private static final String GROUP_ID_PREFIX = "G";
    private static final String PUBLIC_ACCOUNT_ID_PREFIX = "P";
    private static final String SERVER_ACCOUNT_ID_PREFIX = "S";


    public UserBaseResponseVO getUserBaseByMobile(String mobile) {

        UserEntity user = userDAO.findByMobile(mobile);
        if (user != null) {
            UserBaseResponseVO userBaseResponseVO = new UserBaseResponseVO();
            BeanUtils.copyProperties(user, userBaseResponseVO);
            return userBaseResponseVO;
        }
        return null;
    }

    public UserBaseResponseVO getUserBaseByName(String name) {

        UserEntity user = userDAO.findByUserName(name);
        if (user != null) {
            UserBaseResponseVO userBaseResponseVO = new UserBaseResponseVO();
            BeanUtils.copyProperties(user, userBaseResponseVO);
            return userBaseResponseVO;
        }
        return null;
    }

    /**
     * 批量走redis查询用户详情
     *
     * @param userIds
     * @return
     */
    public Map<String, UserBaseResponseInfoVO> batchGet(Set<String> userIds) {

        return this.batchGetUserInfoFromCache(new ArrayList<>(userIds));
    }

    /**
     *
     *
     * @param account
     * @return
     */
    public UserBaseResponseInfoVO queryAccount(String account) {
        if (StringUtils.isBlank(account)) {
            return null;
        }
        String cacheKey = RedisKeyEnum.USER_INFO_BY_MOBILE_CACHE.getKey(account);
        String cache = redisService.get(cacheKey);
        if (StringUtils.isNotBlank(cache)) {
            if (ObjectUtils.equals(WebConstant.CACHE_NONE, cache)) {
                return null;
            }
            return JsonUtil.fromJson(cache, UserBaseResponseInfoVO.class);
        }
        // 刷新BY手机号用户详情缓存
        return this.refreshUserCacheByMobile(account);
    }

    /**
     * 账号密码校验
     * @param account
     * @param password
     * @return
     */
    public boolean validateAccountPassword(String account, String password) {

        UserEntity user = userDAO.findByUserId(account);
        Assert.notNull(user, "账号不存在");
        return MD5Utils.md5(password.concat(WebConstant.MD5_SALT)).equals(user.getPassword());
    }

    /**
     * 刷新用户详情缓存，基于手机号查询
     *
     * @param account
     * @return
     */
    private UserBaseResponseInfoVO refreshUserCacheByMobile(String account) {
        String cacheKey = RedisKeyEnum.USER_INFO_BY_MOBILE_CACHE.getKey(account);
        UserEntity userEntity = userDAO.findByMobile(account);
        if (userEntity == null) {
            redisService.set(cacheKey, WebConstant.CACHE_NONE, 5 * 60);
            return null;
        }
        UserBaseResponseInfoVO vo = convertBaseVo(userEntity);
        redisService.set(cacheKey, JsonUtil.toJsonString(vo), RedisKeyEnum.USER_INFO_BY_MOBILE_CACHE.getExpireTime());
        return vo;
    }

    public APIPageResponseBean<UserBaseResponseInfoVO> page(String userId, Integer roleCode, String userName, String mobile,
                                                            int pageNo, int pageSize) {

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "id"));
        Specification<UserEntity> specification = createAccountQuerySpecification(userId, roleCode, userName, mobile);
        Page<UserEntity> userEntityPage = userDAO.findAll(specification, pageable);
        List<UserEntity> userEntities = userEntityPage.getContent();
        List<UserBaseResponseInfoVO> userBaseVoList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(userEntities)) {
            userBaseVoList = userEntities.stream().map(UserService::convertBaseVo).collect(Collectors.toList());
        }
        return APIPageResponseBean.success(pageNo, pageSize, userEntityPage.getTotalElements(), userBaseVoList);
    }

    private Specification createAccountQuerySpecification(String userId, Integer roleCode, String userName, String mobile) {
         return  ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (roleCode != null) {
                predicates.add(criteriaBuilder.equal(root.get("roleCode").as(Integer.class), roleCode));
            }
            if (StringUtils.isNotBlank(userId)) {
                predicates.add(criteriaBuilder.equal(root.get("userId").as(String.class), userId));
            }
            if (StringUtils.isNotBlank(userName)) {
                 predicates.add(criteriaBuilder.like(root.get("userName").as(String.class), "%" + userName + "%"));
            }
            if (StringUtils.isNotBlank(mobile)) {
                predicates.add(criteriaBuilder.equal(root.get("mobile").as(String.class), mobile));
            }
            Predicate[] pre = new Predicate[predicates.size()];
            criteriaQuery.where(predicates.toArray(pre));
            return criteriaQuery.getRestriction();
        });
    }

    /**
     * 用户注册 - 注册成功自动登录
     * @param photo
     * @param userName
     * @param mobile
     * @param password
     * @return
     */
    // 这里其实不需要事务，因为想分享事务管理器的用法和场景，刻意添加了事务注解
    @Transactional
    public boolean registry(String photo, String userName, String mobile, String password) {
        // 参数校验
        this.validateRegistryParam(userName, mobile);
        // 注册信息入库
        String uid = this.registryUser2DB(userName, photo, mobile, password);
        // 默认添加我的AI助手
        SpringContextUtil.getBean(User2AIAgentSenderAccountRelationService.class).subscribe(uid, WebConstant.AI_AGENT_ID);
        SpringContextUtil.getBean(User2ServerAccountRelationService.class).subscribe(uid, WebConstant.WEBCHAT_PAY_ID);
        // 注册成功，事务结束后刷新用户缓存信息
        TransactionSyncManagerUtil.registerSynchronization(() -> {
            // 刷新用户缓存
            this.refreshAndGetUserEntityFromCache(uid);
        });
        return true;
    }

    private void validateRegistryParam(String userName, String mobile) {
        if (hasUserInfo(mobile)) {
            throw new BusinessException("用户已存在");
        }
        if (!Pattern.matches("^[0-9a-zA-Z_]{1,}$", mobile)) {
            throw new BusinessException("账号格式不合法");
        }
        if (StringUtils.isBlank(userName) || userName.length() < 2 || userName.length() > 12) {
            throw new BusinessException("用户名不合法：2~12位字符");
        }
        if (userNameIsExist(userName)) {
            throw new BusinessException("用户名已经被占用，换一个吧");
        }
    }

    public UserBaseResponseInfoVO queryByMobile(String mobile) {
        UserEntity userEntity = userDAO.findByMobile(mobile);
        return this.convertBaseVo(userEntity);
    }

    public APIPageResponseBean<List<UserSafeResponseInfoVO>> getUserList(Integer roleCode, int pageNo, int pageSize) {
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "id"));
        Page<UserEntity> userEntities;
        if (roleCode == null) {
            userEntities = userDAO.findAll(pageable);
        } else {
            userEntities = userDAO.findAllByRoleCode(roleCode, pageable);
        }
        List<UserSafeResponseInfoVO> userSafeResponseInfoVOS = new ArrayList<>();
        if (userEntities != null && CollectionUtils.isNotEmpty(userEntities.getContent())) {
            userEntities.getContent().stream().forEach(u -> {
                UserSafeResponseInfoVO userSafeResponseInfoVO = this.convertSafeVo(u);
                userSafeResponseInfoVOS.add(userSafeResponseInfoVO);
            });
        }
        return APIPageResponseBean.success(pageNo, pageSize, userEntities.getTotalElements(), userSafeResponseInfoVOS);
    }

    /**
     * 创建机器人
     *
     * 复用用户信息表，账号角色为 ROBOT
     */
    @Transactional
    public void createRobot(CreateRobotRequestVO requestPram) {
        String robotNumber = requestPram.getRobotNumber();
        String robotName = requestPram.getRobotName();
        String robotRole = requestPram.getRobotRole();
        String createUserId = requestPram.getCreateUserId();
        String robotPhoto = requestPram.getRobotPhoto();
        UserEntity userEntity = userDAO.findByMobile(robotName);
        Assert.isTrue(userEntity == null, "机器人账号已存在！");
        /**
         * 防快速点击
         */
        String key = RedisKeyEnum.CREATE_ROBOT_LIMIT.getKey(createUserId);
        boolean lockResult = redisService.installDistributedLock(key, WebConstant.CACHE_NONE, RedisKeyEnum.CREATE_ROBOT_LIMIT.getExpireTime());
        Assert.isTrue(lockResult, "创建中请稍等！请勿频繁点击");
        /**
         * 创建机器人
         */
        robotRole = StringUtils.isBlank(robotRole) ? "全能助手" : robotRole;
        this.registryRobot2DB(requestPram.getId(), robotNumber, robotName, robotRole, robotPhoto, createUserId);
    }

    /**
     * 创建公众号
     *
     * 复用用户信息表，账号角色为 PUBLIC_ACCOUNT
     */
    @Transactional
    public void createPublicAccount(CreateAccountRequestVO requestVO) {
        String account = requestVO.getAccount();
        String accountName = requestVO.getAccountName();
        String signature = requestVO.getSignature();
        String photo = requestVO.getAccountPhoto();
        String createUserId = requestVO.getCreateUserId();
        /**
         * 防快速点击
         */
        String key = RedisKeyEnum.CREATE_ACCOUNT_LIMIT.getKey();
        boolean lockResult = redisService.installDistributedLock(key, WebConstant.CACHE_NONE, RedisKeyEnum.CREATE_ACCOUNT_LIMIT.getExpireTime());
        Assert.isTrue(lockResult, "创建中请稍等！请勿频繁点击");
        // 创建机器人
        this.registryPublicAccount2DB(account, accountName, photo, signature, createUserId);
    }

    /**
     * 创建服务号
     *
     * @param requestVO
     */
    @Transactional
    public void createServerAccount(CreateAccountRequestVO requestVO) {
        String account = requestVO.getAccount();
        String accountName = requestVO.getAccountName();
        String signature = requestVO.getSignature();
        String photo = requestVO.getAccountPhoto();
        String createUserId = requestVO.getCreateUserId();
        /**
         * 防快速点击
         */
        String key = RedisKeyEnum.CREATE_ACCOUNT_LIMIT.getKey();
        boolean lockResult = redisService.installDistributedLock(key, WebConstant.CACHE_NONE, RedisKeyEnum.CREATE_ACCOUNT_LIMIT.getExpireTime());
        Assert.isTrue(lockResult, "创建中请稍等！请勿频繁点击");
        this.registryServerAccount2DB(account, accountName, photo, signature, createUserId);
    }

    /**
     * 查询用户加入的所有群聊
     *
     * @param userId
     * @return
     */
    public List<GroupResponseInfoVO> listUserAttendGroupsFromCache(String userId) {
        if (StringUtils.isBlank(userId)) {
            return Collections.emptyList();
        }
        List<String> groupIds = this.getGroupUserIdsFromCacheByUserId(userId);
        if (CollectionUtils.isEmpty(groupIds)) {
            return Collections.emptyList();
        }
        List<UserBaseResponseInfoVO> groups = this.batchGetUserListInfoFromCache(groupIds);
        return groups.stream().map(g -> {
            GroupResponseInfoVO groupResponseInfoVO = new GroupResponseInfoVO();
            groupResponseInfoVO.setId(g.getId());
            groupResponseInfoVO.setGroupId(g.getUserId());
            groupResponseInfoVO.setGroupName(g.getUserName());
            // 这里性能可能存在问题，最好不要在遍历中查询，当前为了省事，先这么干了
            groupResponseInfoVO.setUserCount(getGroupUserIdsFromCache(g.getUserId()).size());
            return groupResponseInfoVO;
        }).collect(Collectors.toList());
    }

    /**
     * 查询群组下所有用户信息列表
     *
     * @param groupId
     * @return
     */
    public List<UserBaseResponseInfoVO> getGroupUsersFromCache(String groupId, String userId) {
        List<String> groupIds = getGroupUserIdsFromCache(groupId);
        if (CollectionUtils.isEmpty(groupIds)) {
            return Collections.emptyList();
        }
        Assert.isTrue(groupIds.contains(userId), "无群组查看权限！");
        // 缓存中批量查询用户信息
        return this.batchGetUserListInfoFromCache(groupIds);
    }

    /**
     * 查询群组下所有用户ID列表
     *
     * @param groupId
     * @return
     */
    public List<String> getGroupUserIdsFromCache(String groupId) {
        String key = RedisKeyEnum.GROUP_USER_ID_LIST_CACHE.getKey(groupId);
        // redis中取群组下所有用户ID列表
        Set<String> userIds = redisService.zreverseRange(key, 0, Integer.MAX_VALUE);
        if (CollectionUtils.isEmpty(userIds)) {
            userIds = refreshGroupUserIdListCache(groupId);
        }
        return new ArrayList<>(userIds);
    }

    /**
     * 查询用户加入的所有群组
     *
     *
     * @param userId
     * @return
     */
    public List<String> getGroupUserIdsFromCacheByUserId(String userId) {
        String key = RedisKeyEnum.USER_ATTEND_GROUP_ID_LIST_CACHE.getKey(userId);
        // redis中取群组下所有用户ID列表
        String cache = redisService.get(key);
        if (StringUtils.isNotBlank(cache)) {
            return JsonUtil.fromJson(cache, new TypeReference<List<String>>() {});
        }
        return this.refreshUserAttendGroupIdListCache(userId);
    }

    /**
     * 删除用加入的群组列表缓存
     *
     * @param userIds
     */
    private void removeGroupUserIdsFromCacheByUserId(Set<String> userIds) {
        for (String userId : userIds) {
            String key = RedisKeyEnum.USER_ATTEND_GROUP_ID_LIST_CACHE.getKey(userId);
            redisService.remove(key);
        }
    }

    private List<String> refreshUserAttendGroupIdListCache(String userId) {
        String key = RedisKeyEnum.USER_ATTEND_GROUP_ID_LIST_CACHE.getKey(userId);
        List<GroupUserEntity> groupUserEntities = groupUserDAO.findAllByUserIdAndStatusOrderByIdDesc(userId, UserStatusEnum.ENABLE.getStatus());
        List<String> groupIds = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(groupUserEntities)) {
            groupIds = groupUserEntities.stream().map(GroupUserEntity::getGroupId).collect(Collectors.toList());
        }
        redisService.set(key, JsonUtil.toJsonString(groupIds), RedisKeyEnum.USER_ATTEND_GROUP_ID_LIST_CACHE.getExpireTime());
        return groupIds;
    }

    /**
     * 刷新群组下用户列表缓存
     */
    private Set<String> refreshGroupUserIdListCache(String groupId) {
        String key = RedisKeyEnum.GROUP_USER_ID_LIST_CACHE.getKey(groupId);
        List<GroupUserEntity> groupUserEntities =
                groupUserDAO.findAllByGroupIdAndStatusOrderByIdDesc(groupId, UserStatusEnum.ENABLE.getStatus());
        // 先清空
        redisService.remove(key);
        if (CollectionUtils.isEmpty(groupUserEntities)) {
            return Collections.emptySet();
        }
        Set<ZSetOperations.TypedTuple<String>> typedTupleSet = new HashSet<>();
        for (GroupUserEntity groupUser : groupUserEntities) {
            String value = groupUser.getUserId().toString();
            double score = groupUser.getId();
            ZSetOperations.TypedTuple<String> tuple = new DefaultTypedTuple<>(value, score);
            typedTupleSet.add(tuple);
        }
        redisService.zadd(key, typedTupleSet, RedisKeyEnum.GROUP_USER_ID_LIST_CACHE.getExpireTime());
        return groupUserEntities.stream().map(GroupUserEntity::getUserId).collect(Collectors.toSet());
    }

    /**
     * 添加用户到群组
     *
     * @param groupId
     * @param userIdSet
     * @param createUserId
     */
    private void addUsers2Group(String groupId, Set<String> userIdSet, String createUserId) {
        if (CollectionUtils.isEmpty(userIdSet)) {
            return;
        }
        Date now = new Date();
        List<GroupUserEntity> groupUserEntities = userIdSet.stream().map(
                uid -> {
                    GroupUserEntity groupUserEntity = new GroupUserEntity();
                    groupUserEntity.setGroupId(groupId);
                    groupUserEntity.setUserId(uid);
                    groupUserEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
                    groupUserEntity.setCreateBy(createUserId);
                    groupUserEntity.setCreateDate(now);
                    return groupUserEntity;
                }
        ).collect(Collectors.toList());
        // 批量添加用户到群组
        groupUserDAO.saveAll(groupUserEntities);
    }

    /**
     * 注册用户到数据库
     *
     * @param userName
     * @param photo
     * @param mobile
     * @param password
     */
    private String registryUser2DB(String userName, String photo, String mobile, String password) {
        UserEntity userEntity = new UserEntity();
        String userId = IDGenerateUtil.createId(USER_ID_PREFIX);
        userEntity.setUserId(userId);
        userEntity.setUserName(StringUtil.handleSpecialHtmlTag(userName));
        userEntity.setPhoto(photo);
        userEntity.setMobile(mobile);
        userEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
        userEntity.setPassword(md5Pwd(password));
        userEntity.setRoleCode(RoleCodeEnum.USER.getCode());
        userEntity.setCreateBy(userId);
        userEntity.setSignature("暂无签名~");
        // 注册用户
        return userDAO.save(userEntity).getUserId();
    }

    /**
     * 创建群组到数据库
     *
     * @param groupName
     * @param createUserId
     */
    public String registerGroup2DB(String groupName, String groupPhoto, String createUserId) {
        UserEntity userEntity = new UserEntity();
        String groupId = IDGenerateUtil.createId(GROUP_ID_PREFIX);
        userEntity.setUserId(groupId);
        userEntity.setUserName(StringUtil.handleSpecialHtmlTag(groupName));
        userEntity.setPhoto(groupPhoto);
        userEntity.setMobile("-");
        userEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
        // 密码设置为用户ID, 本身也不支持登录
        userEntity.setPassword(md5Pwd(groupId));
        userEntity.setRoleCode(RoleCodeEnum.GROUP.getCode());
        userEntity.setCreateBy(createUserId);
        // 创建群组
        userDAO.save(userEntity);
        // 注册成功，事务结束后刷新群组缓存
        TransactionSyncManagerUtil.registerSynchronization(() ->
                this.refreshAndGetUserEntityFromCache(groupId));
        return groupId;
    }

    /**
     * 创建机器人到数据库
     *
     * @param robotName
     * @param createUserId
     */
    private String registryRobot2DB(Long id, String robotNumber, String robotName, String role, String robotPhoto, String createUserId) {
        UserEntity userEntity;

        if (id != null) {
            userEntity = userDAO.findById(id).orElse(null);
        } else {
            userEntity = new UserEntity();
            String robotId = IDGenerateUtil.createId(ROBOT_ID_PREFIX);
            userEntity.setUserId(robotId);
            // 密码设置为用户ID, 本身也不支持登录
            userEntity.setPassword(md5Pwd(robotId));
            userEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
            userEntity.setRoleCode(RoleCodeEnum.ROBOT.getCode());
            userEntity.setCreateBy(createUserId);
        }
        userEntity.setUserName(robotName);
        userEntity.setPhoto(robotPhoto);
        userEntity.setMobile(robotNumber);
        userEntity.setSignature(role);
        // 注册用户
        userEntity = userDAO.save(userEntity);
        String robotId = userEntity.getUserId();
        // 注册成功，事务结束后刷新用户缓存信息
        TransactionSyncManagerUtil.registerSynchronization(() -> {
            // 刷新用户缓存
            this.refreshAndGetUserEntityFromCache(robotId);
        });
        return robotId;
    }

    /**
     * 创建公众号
     * @return
     */
    private String registryPublicAccount2DB(String account, String accountName, String accountPhoto,
                                            String accountSignature, String createUserId) {

        UserEntity userEntity = userDAO.findByMobile(account);
        Assert.isTrue(userEntity == null, "公众号已经存在");

        userEntity = new UserEntity();
        String userId = IDGenerateUtil.createId(PUBLIC_ACCOUNT_ID_PREFIX);
        userEntity.setUserId(userId);
        userEntity.setUserName(StringUtil.handleSpecialHtmlTag(accountName));
        userEntity.setPhoto(accountPhoto);
        userEntity.setMobile(account);
        userEntity.setSignature(accountSignature);
        userEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
        // 密码设置为用户ID, 本身也不支持登录
        userEntity.setPassword(md5Pwd(account));
        userEntity.setRoleCode(RoleCodeEnum.PUBLIC_ACCOUNT.getCode());
        userEntity.setCreateBy(createUserId);
        // 注册用户
        userDAO.save(userEntity);
        // 注册成功，事务结束后刷新用户缓存信息
        TransactionSyncManagerUtil.registerSynchronization(() -> {
            // 刷新用户缓存
            this.refreshAndGetUserEntityFromCache(account);
            // 刷新公众号文章列表
            publicAccountService.refreshPublicAccountListCache();
        });
        return userId;
    }

    /**
     * 创建服务号
     *
     * @return
     */
    private String registryServerAccount2DB(String account, String accountName, String accountPhoto,
                                            String accountSignature, String createUserId) {

        UserEntity userEntity = userDAO.findByMobile(account);
        Assert.isTrue(userEntity == null, "公众号已经存在");

        userEntity = new UserEntity();
        String userId = IDGenerateUtil.createId(SERVER_ACCOUNT_ID_PREFIX);
        userEntity.setUserId(userId);
        userEntity.setUserName(StringUtil.handleSpecialHtmlTag(accountName));
        userEntity.setPhoto(accountPhoto);
        userEntity.setMobile(account);
        userEntity.setSignature(accountSignature);
        userEntity.setStatus(UserStatusEnum.ENABLE.getStatus());
        // 密码设置为用户ID, 本身也不支持登录
        userEntity.setPassword(md5Pwd(account));
        userEntity.setRoleCode(RoleCodeEnum.SERVER_ACCOUNT.getCode());
        userEntity.setCreateBy(createUserId);
        // 注册用户
        userDAO.save(userEntity);
        // 注册成功，事务结束后刷新用户缓存信息
        TransactionSyncManagerUtil.registerSynchronization(() -> {
            // 刷新用户缓存
            this.refreshAndGetUserEntityFromCache(account);
            // 刷新公众号文章列表
            publicAccountService.refreshPublicAccountListCache();
        });
        return userId;
    }

    /**
     * 判断用户是否存在
     * @param mobile
     * @return
     */
    private boolean hasUserInfo(String mobile) {
        return userDAO.findByMobile(mobile) != null;
    }

    private boolean userNameIsExist(String userName) {
        return userDAO.findByUserName(userName) != null;
    }

    /**
     * 获取当前登录用户信息
     * @return
     */
    public UserBaseResponseInfoVO getCurrentUserInfo() {

        return getLoginUserBaseInfoFromCache();
    }

    public UserBaseResponseInfoVO getLoginUserBaseInfoFromCache() {
        String userId = this.getLoginCurrentUserId();
        return getUserBaseInfoByUserIdFromCache(userId);
    }

    /**
     * 查询用户详情
     *
     * @param userId
     * @return
     */
    public UserBaseResponseInfoVO getUserInfoByUserId(String userId) {

        return getUserBaseInfoByUserId(userId);
    }

    /**
     * 查询指定订阅用户
     *
     * @param relationType 关系类型
     * @param account
     * @return
     */
    public Set<String> getAllSubscriberByAccount(Integer relationType, String account) {

        return AccountRelationFactory.getServiceByType(relationType).getAllSubscriber(account);
    }

    /**
     * 判断userAccount是否订阅account
     *
     * @param relationType
     * @param userAccount
     * @param account
     * @return
     */
    public Boolean isSubscribe(Integer relationType, String userAccount, String account) {

        return AccountRelationFactory.getServiceByType(relationType).isSubscribe(userAccount, account);
    }

    /**
     * 批量校验账号是否存在订阅关系
     *
     * @param relationType
     * @param userAccounts
     * @param account
     * @return
     */
    public Map<String, Boolean> batchIsSubscribe(Integer relationType, List<String> userAccounts, String account) {

        return AccountRelationFactory.getServiceByType(relationType).batchIsSubscribe(userAccounts, account);
    }

    /**
     * 用户信息编辑
     *
     * @param updateUserInfoRequest
     * @return
     */
    public boolean updateUserInfo(UpdateUserInfoRequestVO updateUserInfoRequest) {
        // 更新数据库
        UserEntity userEntity = userDAO.findByUserId(updateUserInfoRequest.getUserId());
        Assert.notNull(userEntity, "用户信息更新失败: 用户不存在！");
        // 支持单独修改头像或者昵称
        if (StringUtils.isNotBlank(updateUserInfoRequest.getUserName())) {
            userEntity.setUserName(updateUserInfoRequest.getUserName());
        }
        if (StringUtils.isNotBlank(updateUserInfoRequest.getPhoto())) {
            userEntity.setPhoto(updateUserInfoRequest.getPhoto());
        }
        userDAO.save(userEntity);
        // 刷新用户信息缓存
        this.refreshUserDetailCache(userEntity);
        return true;
    }

    public UserBaseResponseInfoVO getUserBaseInfoByUserId(String userId) {
        if (StringUtils.isBlank(userId)) {
            return null;
        }
        return getUserBaseInfoByUserIdFromCache(userId);
    }

    private UserBaseResponseInfoVO getUserBaseInfoByUserIdFromCache(String userId) {
        if (userId == null) {
            return null;
        }
        UserEntity entity = this.refreshAndGetUserEntityFromCache(userId);
        UserBaseResponseInfoVO userBaseResponseInfoVO = this.convertBaseVo(entity);
        if (userBaseResponseInfoVO == null) {
            return null;
        }
        return userBaseResponseInfoVO;
    }

    /**
     * 强制刷新用户信息缓存
     * @param userId
     */
    private void refreshUserDetailCache(String userId) {
        this.refreshAndGetUserEntityFromCache(userId, true);
    }

    private UserEntity refreshAndGetUserEntityFromCache(String userId, boolean ... refreshCache) {
        String key = RedisKeyEnum.USER_INFO_CACHE.getKey(userId);
        String val = redisService.get(key);
        boolean refresh = refreshCache != null && refreshCache.length > 0 && refreshCache[0];
        if (!refresh && StringUtils.isNotBlank(val)) {
            return JsonUtil.fromJson(val, UserEntity.class);
        }
        UserEntity userEntity = userDAO.findByUserId(userId);
        if (userEntity != null) {
            this.refreshUserDetailCache(userEntity);
        }
        return userEntity;
    }

    /**
     * 刷新用户详情redis缓存
     *
     * @param userEntity
     */
    private void refreshUserDetailCache(UserEntity userEntity) {
        if (userEntity == null) {
            return;
        }
        String key = RedisKeyEnum.USER_INFO_CACHE.getKey(userEntity.getUserId());
        redisService.set(key, JsonUtil.toJsonString(userEntity),  RedisKeyEnum.USER_INFO_CACHE.getExpireTime());
    }

    /***
     * 取当前用户ID，且未登录时直接抛出未登录异常
     * @return
     */
    public String getLoginCurrentUserIdOrElseThrowUnLoginExp() {
        String userId = getLoginCurrentUserId();
        if (StringUtils.isNotBlank(userId)) {
            return userId;
        }
        throw new BusinessException(APIErrorCommonEnum.USER_UN_LOGIN);
    }

    /***
     * 更新用户角色
     * @param userId
     * @param roleCode
     */
    public void updateUserRole(String userId, Integer roleCode) {
        UserEntity userEntity = userDAO.findByUserId(userId);
        Assert.isTrue(userEntity != null, "更新失败，用户不存在!");
        userEntity.setRoleCode(roleCode);
        userDAO.save(userEntity);
        /***
         * 刷新用户信息缓存
         */
        this.refreshUserDetailCache(userId);
    }

    /**
     * 获取当前登录的用户ID
     * @return
     */
    public String getLoginCurrentUserId() {
        return SessionHelper.getCurrentUserId();
    }

    private String md5Pwd(String password) {
        return MD5Utils.md5(password.concat(WebConstant.MD5_SALT));
    }

    /**
     * 批量查询用户缓存
     *
     * @param userIdList
     * @return
     */
    public Map<String, UserBaseResponseInfoVO> batchGetUserInfoFromCache(List<String> userIdList) {
        Map<String, UserBaseResponseInfoVO> userBaseResponseInfoVOMap = new HashMap<>();
        if (CollectionUtils.isEmpty(userIdList)) {
            return userBaseResponseInfoVOMap;
        }
        List<String> cacheKeys = userIdList.stream().map(k ->
                RedisKeyEnum.USER_INFO_CACHE.getKey(String.valueOf(k))).collect(Collectors.toList());
        Map<String, String> multiResultMap = redisService.mgetAndParseMap(cacheKeys);
        for (int i = 0; i < userIdList.size(); i++) {
            String userId = userIdList.get(i);
            String cacheKey = cacheKeys.get(i);
            String cache = multiResultMap.get(cacheKey);
            UserBaseResponseInfoVO userBaseResponseInfoVO;
            if (StringUtils.isNotBlank(cache)) {
                userBaseResponseInfoVO = JsonUtil.fromJson(cache, UserBaseResponseInfoVO.class);
            } else {
                userBaseResponseInfoVO = getUserBaseInfoByUserId(userId);
            }
            userBaseResponseInfoVOMap.put(userId, userBaseResponseInfoVO);
        }
        return userBaseResponseInfoVOMap;
    }

    public List<UserBaseResponseInfoVO> batchGetUserListInfoFromCache(Set<String> userIdSet) {
        return this.batchGetUserListInfoFromCache(new ArrayList<>(userIdSet));
    }

    public List<UserBaseResponseInfoVO> batchGetUserListInfoFromCache(List<String> userIdList) {
        if (CollectionUtils.isEmpty(userIdList)) {
            return Collections.emptyList();
        }
        List<String> cacheKeys = userIdList.stream().map(k ->
                RedisKeyEnum.USER_INFO_CACHE.getKey(String.valueOf(k))).collect(Collectors.toList());
        Map<String, String> multiResultMap = redisService.mgetAndParseMap(cacheKeys);
        List<UserBaseResponseInfoVO> userBaseResponseInfoVOList = new ArrayList<>();
        for (int i = 0; i < userIdList.size(); i++) {
            String userId = userIdList.get(i);
            String cacheKey = cacheKeys.get(i);
            String cache = multiResultMap.get(cacheKey);
            UserBaseResponseInfoVO userBaseResponseInfoVO;
            if (StringUtils.isNotBlank(cache)) {
                userBaseResponseInfoVO = JsonUtil.fromJson(cache, UserBaseResponseInfoVO.class);
            } else {
                userBaseResponseInfoVO = getUserBaseInfoByUserId(userId);
            }

            if (userBaseResponseInfoVO != null) {
                userBaseResponseInfoVOList.add(userBaseResponseInfoVO);
            }
        }
        return userBaseResponseInfoVOList;
    }

    /**
     * 创建系统账号
     * @return
     */
    public UserBaseResponseInfoVO getSystemUserInfo() {
        UserBaseResponseInfoVO userBaseResponseInfoVO = new UserBaseResponseInfoVO();
        userBaseResponseInfoVO.setUserId(WebConstant.SYSTEM_USER_ID);
        userBaseResponseInfoVO.setUserName("系统");
        userBaseResponseInfoVO.setPhoto("");
        return userBaseResponseInfoVO;
    }

    public static UserBaseResponseInfoVO convertBaseVo(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        UserBaseResponseInfoVO vo = new UserBaseResponseInfoVO();
        BeanUtils.copyProperties(entity, vo);
        vo.setMobile(CommonUtils.mobileEncrypt(entity.getMobile()));
        return vo;
    }

    public static UserSafeResponseInfoVO convertSafeVo(UserEntity entity) {
        if (entity == null) {
            return null;
        }
        UserSafeResponseInfoVO vo = new UserSafeResponseInfoVO();
        BeanUtils.copyProperties(entity, vo);
        if (entity.getCreateDate() != null) {
            vo.setRegistryTime(entity.getCreateDate().getTime());
            vo.setRegistryTimeStr(DateUtils.getDate2String(entity.getCreateDate()));
        }
        return vo;
    }
}
